#include "ota_version.h"
#include "log/log.h"
#include <unistd.h>
#include "ota_http.h"
#include "cjson/cJSON.h"
#include <openssl/sha.h>
#include <string.h>
#include <sys/reboot.h>
#include <stdlib.h>

/**
 * 获取文件的SHA1哈希值（40位16进制字符串）
 * 相同文件返回相同的哈希值，可用于判断文件是否相同
 * linux命令生成：sha1sum 文件名
 * 此函数可以利用AI工具帮我们生成
 */
static char *get_file_sha(char *filepath)
{
    FILE *file = fopen(filepath, "rb");
    if (!file)
    {
        perror("Failed to open file");
        return NULL;
    }

    unsigned char hash[SHA_DIGEST_LENGTH];
    SHA_CTX sha1;
    SHA1_Init(&sha1);

    const int bufSize = 32768;
    unsigned char *buffer = (unsigned char *)malloc(bufSize);
    if (!buffer)
    {
        perror("Failed to allocate memory");
        fclose(file);
        return NULL;
    }

    int bytesRead;
    while ((bytesRead = fread(buffer, 1, bufSize, file)) > 0)
    {
        SHA1_Update(&sha1, buffer, bytesRead);
    }

    SHA1_Final(hash, &sha1);
    fclose(file);
    free(buffer);

    char *outputBuffer = (char *)malloc(SHA_DIGEST_LENGTH * 2 + 1);
    if (!outputBuffer)
    {
        perror("Failed to allocate memory");
        return NULL;
    }

    for (int i = 0; i < SHA_DIGEST_LENGTH; i++)
    {
        sprintf(outputBuffer + (i * 2), "%02x", hash[i]);
    }

    return outputBuffer;
}

/**
 * @brief 获取本地版本号
 *
 * @return char* 版本号字符串
 */
char *ota_version_getLocalVersion(void)
{
    // 读取本地版本号
    char *local_version = (char *)malloc(10);
    if (!local_version)
    {
        log_error("申请内存失败");
        return NULL;
    }

    // 读取本地版本号
    FILE *file = fopen(OTA_LOCAL_VERSION_FILE, "r");
    if (file)
    {
        int major, minor, patch;
        // 读取版本号
        if (fscanf(file, "%d\n%d\n%d", &major, &minor, &patch) == 3)
        {
            // 关闭文件
            fclose(file);
            // 格式化版本号字符串
            snprintf(local_version, 10, "%d.%d.%d", major, minor, patch);
            return local_version;
        }
        else
        {
            log_error("本地版本号文件格式不正确");
            fclose(file);
            free(local_version);
            return NULL;
        }
    }
    else
    {
        // 文件不存在，创建文件并写入默认版本号
        file = fopen(OTA_LOCAL_VERSION_FILE, "w");
        if (file)
        {
            fprintf(file, "1\n0\n0\n"); // 默认版本号
            fclose(file);
            snprintf(local_version, 10, "1.0.0");
            return local_version;
        }
        else
        {
            log_error("本地版本号文件创建失败");
            free(local_version);
            return NULL;
        }
    }
}

/**
 * @brief 检查是否有新版本
 *
 * @return int 0表示没有新版本，1表示有新版本
 */
int ota_version_checkUpdate(void)
{
    // 1. 获取远程最新版本信息
    char *json = ota_http_getJson(OTA_URL_FILEINFO);
    // 2. 解析json, 得到各级版本号
    cJSON *root = cJSON_Parse(json);
    int major = cJSON_GetObjectItem(root, "major")->valueint;
    int minor = cJSON_GetObjectItem(root, "minor")->valueint;
    int patch = cJSON_GetObjectItem(root, "patch")->valueint;
    log_debug("远程版本号为： %d.%d.%d", major, minor, patch);
    // 3. 获取本地版本号
    char *local_version = ota_version_getLocalVersion();
    if (!local_version)
    {
        log_error("获取本地版本号失败");
        return -1;
    }
    log_debug("当前版本号为： %s", local_version);

    // 3. 比较版本号， 如果当前版本号不小于远程版本号，则不需要更新
    if (local_version[0] - '0' > major || (local_version[0] - '0' == major && local_version[1] - '0' > minor) || (local_version[0] - '0' > minor == major && local_version[0] - '0' == minor && local_version[2] - '0' >= patch))
    {
        log_debug("当前版本已经是最新版本，不需要更新");
        return 0;
    }
    // 4. 下载远程固件/程序
    // 删除本地文件
    unlink(OTA_LOCAL_PATH);
    int result = ota_http_download(OTA_URL_DOWNLOAD, OTA_LOCAL_PATH);
    if (result != 0)
    {
        log_error("请求下载固件失败");
        return -1;
    }
    // 5. 检查下载的文件是否完整
    char *remote_hash = cJSON_GetObjectItem(root, "sha1")->valuestring;
    char *local_hash = get_file_sha(OTA_LOCAL_PATH);
    if (strcmp(remote_hash, local_hash) != 0)
    { // 校验失败
        log_error("下载的文件校验失败");

        cJSON_Delete(root);
        free(local_hash);
        unlink(OTA_LOCAL_PATH); // 删除本地文件
        return -1;
    }

    // 6. 下载成功，更新本地版本号
    // 打开文件流，写入版本号
    FILE *version_file = fopen(OTA_LOCAL_VERSION_FILE, "w");
    if (version_file)
    {
        fprintf(version_file, "%d\n%d\n%d\n", major, minor, patch);
        fclose(version_file);
    }
    else
    {
        log_error("本地版本号文件更新失败");
        cJSON_Delete(root);
        free(local_version);
        free(local_hash);
        return -1;
    }

    int ret = ota_version_printVersion();
    if (ret != 0)
    {
        log_error("打印版本号失败");
        cJSON_Delete(root);
        free(local_version);
        free(local_hash);
        return -1;
    }

    cJSON_Delete(root);
    free(local_version);
    free(local_hash);

    // 7. 重启系统，运行最新的固件
    log_debug("下载校验完成，开始重启系统");
    reboot(RB_AUTOBOOT); // 重启系统必须是root用户， 普通用户不会重启

    return 0;
}

/**
 * @brief 日常检查是否有最新版本
 *
 * @return int 0表示没有新版本，1表示有新版本
 */
int ota_version_checkUpdateDaily(void)
{
    while (1)
    {
        ota_version_checkUpdate();
        sleep(24 * 60 * 60); // 每天检查一次
    }

    return 0;
}

/**
 * @brief 打印版本号
 *
 * @return int 0表示成功, -1表示失败
 */
int ota_version_printVersion(void)
{

    char *local_version = ota_version_getLocalVersion();
    if (!local_version)
    {
        log_error("获取本地版本号失败");
        return -1;
    }
    log_debug("当前版本号为： %s", local_version);

    return 0;
}
